import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedList;

import org.zkoss.zkex.zul.South;
import org.zkoss.zul.Div;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Paging;
import org.zkoss.zul.Label;
import org.zkoss.zul.Popup;
import org.zkoss.zul.Menuitem;
import org.zkoss.zk.ui.WrongValueException;

import publicComponent.*;
import administrationPane.HistoryController;

public class DefaultHistoryControllerImpl 
extends Div 
implements HistoryController{
	private Div mainDiv=null;
	private Paging paging=null;
	private Listbox list=null;
	private Toolbar controller=null;
	private South shortcutPane=null;
	
	private String lastKey=null;
	private int theIndex=-1;
	
	private int listSize=0;
	
	private int numPerPage=0;
	private int numPerPageCollapsed=0;
	private int maxHistorySize=0;
	private LinkedList arr=new LinkedList();
	
	public void initialize(){
		mainDiv=self;
		paging=getFellow("ADMIN_HISTORY_PAGING");
		list=getFellow("ADMIN_HISTORY_LIST");
		controller=getFellow("ADMIN_HISTORY_CONTROL_PANE");
		shortcutPane=getFellow("ADMIN_SHORTCUT_PANE");
		numPerPage=Integer.parseInt(list.getAttribute("numberPerPage"));
		numPerPageCollapsed=Integer.parseInt(list.getAttribute("numberPerPageCollapsed"));
		maxHistorySize=Integer.parseInt(list.getAttribute("maxHistorySize"));
	}
	
	public int find(String key){
		list.setSelectedIndex(-1);
		if(lastKey != null && lastKey.equals(key)){
			return search(theIndex,key);
		}else{
			lastKey=new String(key);
			return search(-1,key);
		}
	}
	
	private int search(int starting_point,String key){
		for(int i=starting_point+1;i < listSize;i++)
			if(((String)((Triple)arr.get(i)).getFirst()).indexOf(key) != -1){
				theIndex=i;
				list.setSelectedIndex(i);
				focusOn(i);
				return i;
			}
		
		lastkey=null;
		return -1;
	}
	
	public void addListItem(String name,String time,Memento m){
		Listitem newItem=new Listitem();
		Listcell cell=new Listcell();
		Label lab=new Label(name);
		cell.appendChild(lab);
		newItem.appendChild(cell);
		cell=new Listcell();
		lab=new Label(time);
		cell.appendChild(lab);
		newItem.appendChild(cell);
		list.appendChild(newItem);
		
		//newItem.setAttribute("STATE", "PROCESSING");
		
		listSize++;
		DefaultHistoryControllerImpl.Triple tri=new DefaultHistoryControllerImpl.Triple(name,newItem);
		tri.setMem(m);
		this.arr.addLast(tri);
	}
	
	public void addHistory(String name,Memento m){
		if(listSize == maxHistorySize){
			list.removeChild(list.getFirstChild());
			listSize--;
			arr.removeFirst();
		}
		
		this.addListItem(name,getCurrentTime(),m);
		changePageSize(true);
	}
	
	private String getCurrentTime(){
		Date curr=(new GregorianCalendar()).getTime();
		String format=list.getAttribute("dateFormat");
		if(format == null || format.equals("")){
			SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd");
			return timeFormat.format(curr);
		}else{
			SimpleDateFormat timeFormat = new SimpleDateFormat(format);
			return timeFormat.format(curr);
		}
	}
	
	public void changePageSize(boolean setToLastPage){
		if(setToLastPage)
			list.setSelectedIndex(-1);
		paging.setTotalSize(arr.size());
		paging.setPageSize(shortcutPane.isOpen() ? numPerPage : numPerPageCollapsed);
		if(list.getSelectedIndex() == -1 || setToLastPage)
			paging.setActivePage(paging.getPageCount()-1);
		else paging.setActivePage(list.getSelectedIndex()/paging.getPageSize());
		setPage(paging.getActivePage());
	}
	
	public void setPage(int pageIndex){
		int startingPos=pageIndex*paging.getPageSize();
		int endingPos=startingPos+paging.getPageSize()-1;
		Iterator itr=arr.iterator();
		int i=0;
		while(itr.hasNext()){
			Listitem tempItem=((Listitem)((Triple)itr.next()).getSecond());
			if(i >= startingPos && i <= endingPos)
				tempItem.setVisible(true);
			else tempItem.setVisible(false);
			i++;
		}
	}
	
	private void focusOn(int itemNum){
		paging.setActivePage(itemNum/paging.getPageSize());
		setPage(paging.getActivePage());
	}
	
	private static int testID=0;
	public void testAdd(){
		addHistory("case"+testID);
	}
	
	public void undo(){
		if(list.getSelectedIndex() == -1)
			return;
		
		Triple tri=arr.get(list.getSelectedIndex());
		String state=((Listitem)tri.getSecond()).getAttribute("STATE");
		if(state == null || state.equals("") || state.equals("SUCCESSFUL")){
			Memento m=((Memento)tri.getThird());
			if(m == null)
				recordFailed(((Listitem)tri.getSecond()),list.getAttribute("memoLossMsg"));
			else{ 
				Originator ori=m.getState();
				switch (ori.setMemento(m)){
					case 1:recordSuccessful(((Listitem)tri.getSecond()),list.getAttribute("undoSucceedMsg"));
					   break;
					case 0:recordFailed(((Listitem)tri.getSecond()),list.getAttribute("undoFailedMsg"));
					   break;
					case -1:recordProcessing(((Listitem)tri.getSecond()),list.getAttribute("undoProcessingMsg"));
				}
			}
		}else if(state.equals("FAILED"))
			throw new WrongValueException(((Listitem)tri.getSecond()),list.getAttribute("failedUnacceptableMsg"));
		else throw new WrongValueException(((Listitem)tri.getSecond()),list.getAttribute("processingUnacceptableMsg"));
	}
	
	public void redo(){
		if(list.getSelectedIndex() == -1)
			return;
		
		Triple tri=arr.get(list.getSelectedIndex());
		String state=((Listitem)tri.getSecond()).getAttribute("STATE");
		if(state == null || state.equals("") || state.equals("SUCCESSFUL")){
			Memento m=((Memento)tri.getThird());
			if(m == null || ori == null)
				recordFailed(((Listitem)tri.getSecond()),list.getAttribute("memoLossMsg"));
			else{ 
				Originator ori=m.getState();
				switch (ori.setBackMemento(m)){
					case 1:recordSuccessful(((Listitem)tri.getSecond()),list.getAttribute("undoSucceedMsg"));
					   break;
					case 0:recordFailed(((Listitem)tri.getSecond()),list.getAttribute("undoFailedMsg"));
					   break;
					case -1:recordProcessing(((Listitem)tri.getSecond()),list.getAttribute("undoProcessingMsg"));
				}
			}
		}else if(state.equals("FAILED"))
			throw new WrongValueException(((Listitem)tri.getSecond()),list.getAttribute("failedUnacceptableMsg"));
		else throw new WrongValueException(((Listitem)tri.getSecond()),list.getAttribute("processingUnacceptableMsg"));
	}
	
	public void recordSuccessful(Listitem cmp,Object msg){}
	
	public void recordFailed(Listitem cmp,Object msg){}
	
	public void recordProcessing(Listitem cmp,Object msg){}
	
	private class Triple{
		private Object first,second;
		private Memento third;
		
		public Triple(Object name,Object time){
			this.first=name;
			this.second=time;
		}
		
		public Object getFirst(){
			return first;
		}
		
		public Object getSecond(){
			return second;
		}
		
		public Object getThird(){
			return third;
		}
		
		public void setMem(Memento m){
			this.third=m;
		}
	}
}